package ok;

import static java.util.Map.Entry.comparingByKey;
import static java.util.stream.Collectors.toMap;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.PriorityQueue;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.ToDoubleFunction;
import java.util.function.ToIntFunction;
import java.util.stream.Collector;
import java.util.stream.Collectors;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;

public class ComputeUtils {
	
	/**
	 * 标准曲线，待完善
	 * @param average
	 * @param standard
	 * @param x
	 * @return
	 */
	public static double getNormalValue(float average, float standard, double x)
	{
		if(average==0 || standard==0)return 0;
		return MathUtils.round(100*Math.pow(Math.E,-((x-average)*(x-average))/(2*standard*standard)) / (Math.sqrt(2*Math.PI)*standard), 2);
	}

	/**
	 * 平均分
	 * @param list
	 * @param mapper
	 * @return
	 */
	public static <T> double average(Collection<T> list, ToDoubleFunction<T> mapper) {
		return list.stream().mapToDouble(mapper).average().orElse(0);
	}

	/**
	 * 最高分
	 * @param list
	 * @param mapper
	 * @return
	 */
	public static <T> double top(Collection<T> list, ToDoubleFunction<T> mapper) {
		return list.stream().mapToDouble(mapper).max().orElse(0);
	}

	/**
	 * 最低分
	 * @param list
	 * @param mapper
	 * @return
	 */
	public static <T> double min(Collection<T> list, ToDoubleFunction<T> mapper) {
		return list.stream().mapToDouble(mapper).min().orElse(0);
	}

	/**
	 * 中位数
	 * @param list
	 * @param orderd
	 *            列表是否已经进行了排序，如果没有需要false
	 * @return
	 */
	public static double median(List<Double> list, boolean... orderd) {
		if (list.size() == 0) {
			return 0;
		}
		if (orderd == null || orderd.length == 0 || orderd[0]) {
			int number = list.size();
			double median = 0f;
			int x = number / 2;
			if (number % 2 == 0) {
				return (list.get(x - 1) + list.get(x)) / 2;
			} else {
				median = list.get(x);
			}
			return median;
		}

		// 无序列表快速得到中位数https://www.cnblogs.com/shizhh/p/5746151.html
		int heapSize = list.size() / 2 + 1;
		PriorityQueue<Double> heap = new PriorityQueue<>(heapSize);
		for (int i = 0; i < heapSize; i++) {
			heap.add(list.get(i));
		}

		for (int i = heapSize; i < list.size(); i++) {
			if (heap.peek() < list.get(i)) {
				heap.poll();
				heap.add(list.get(i));
			}
		}

		if (list.size() % 2 == 1) {
			return (double) heap.peek();
		} else {
			return (double) (heap.poll() + heap.peek()) / 2.0;
		}
	}

	/**
	 * 方差
	 * @param list
	 * @param mapper
	 * @param averages
	 * @return
	 */
	public static <T> double varience(Collection<T> list, ToDoubleFunction<T> mapper, double... averages) {
		double average = (averages == null || averages.length == 0) ? average(list, mapper) : averages[0];
		return list.stream().mapToDouble(item -> Math.pow(mapper.applyAsDouble(item) - average, 2)).average().orElse(0);
	}

	/***
	 * 标准差
	 * @param list
	 * @param mapper
	 * @param variences
	 * @return
	 */
	public static <T> double standard(Collection<T> list, ToDoubleFunction<T> mapper, double... variences) {
		if (variences != null && variences.length == 1) {
			return Math.sqrt(variences[0]);
		}
		double varience = varience(list, mapper);
		return Math.sqrt(varience);
	}

	/**
	 * 满分人数
	 * @param list
	 * @param predicate
	 * @return
	 */
	public static <T> int full(Collection<T> list, Predicate<T> predicate) {
		long count = list.stream().filter(predicate).count();
		return Convert.toInt(count);
	}

	/**
	 * 最简单的计算排序
	 * @param map 分数-数量,需要排序
	 * @return 分数-排名
	 */
	private static <T> Map<Double, Integer> computeOrderFromScoreCount(Map<Double, Long> map,boolean...ordereds) {
		boolean ordered = ordereds != null && ordereds.length>0&& ordereds[0];
		if(!ordered) {
			map = map.entrySet().stream().sorted(Collections.reverseOrder(comparingByKey()))
					.collect(toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e2, LinkedHashMap::new));
		}
		
		Map<Double, Integer> studentIDOrder = new HashMap<>();
		long order = 1;
		for (Entry<Double, Long> entry : map.entrySet()) {
			studentIDOrder.put(entry.getKey(), Convert.toInt(order));
			order = order + entry.getValue();
		}
		return studentIDOrder;
	}

	/**
	 * 最简单的计算排序
	 * @param list 分数集合
	 * @return 分数-排名
	 */
	public static <T> Map<Double, Integer> computeOrder(List<Double> list) {
		Map<Double, Long> map = LambdaUtils.groupby(list, x -> x, Collectors.counting());// 每个分对应多少个学生
		return computeOrderFromScoreCount(map);
	}
	
	/**
	 * 个面具具体业务计算排名
	 * @param list 带分数的业务列表数据
	 * @param scoreFun 获取分数的逻辑
	 * @param orderCon 设置排名的逻辑
	 */
	public static <T> void computeOrder(List<T> list, Function<T, Double> scoreFun, BiFunction<T, Integer, T> action) {
		List<Double> scores = LambdaUtils.list2list(list, scoreFun);
		Map<Double, Integer> studentIDOrder = computeOrder(scores);
		list.forEach(item -> {
			int order=studentIDOrder.get(scoreFun.apply(item));
			action.apply(item, order);
		});
	}

	/**
	 *  差异系数，变异系数，最终保留默认2位
	 * @param standard 保留下属位数之后的,4位
	 * @param average 保留小数位数之后的，2位
	 * @return
	 */
	public static double cv(double standard, double average, int... scales) {
		int scale = (scales == null || scales.length == 0) ? 2 : scales[0];
		standard = MathUtils.round(standard, 4);
		average = MathUtils.round(average);
		return MathUtils.round(MathUtils.div(standard, average), scale);
	}

	/**
	 *  全距，最终保留2位
	 * @param max 保留下属位数之后的2位
	 * @param min 保留小数位数之后的，2位
	 * @return
	 */
	public static double range(double max, double min) {
		max = MathUtils.round(max);
		min = MathUtils.round(min);
		return MathUtils.round(MathUtils.sub(max, min));
	}

	/**
	 *  难度,默认保留4位
	 * @param average 保留下属位数之后的2位
	 * @param fullScore 保留小数位数之后的，2位
	 * @return
	 */
	public static double difficulty(double average, double fullScore, int... scales) {
		int scale = (scales == null || scales.length == 0) ? 4 : scales[0];
		average = MathUtils.round(average);
		fullScore = MathUtils.round(fullScore);
		return MathUtils.round(MathUtils.div(average, fullScore), scale);
	}

	/**
	 * 离均差,默认保留4位
	 * @param average1 保留下属位数之后的2位
	 * @param average2 保留小数位数之后的，2位
	 * @return
	 */
	public static double dfa(double average1, double average2, int... scales) {
		int scale = (scales == null || scales.length == 0) ? 4 : scales[0];
		average1 = MathUtils.round(average1);
		average2 = MathUtils.round(average2);
		return MathUtils.round(MathUtils.sub(average1, average2), scale);
	}

	/**
	 * 离均差率,默认保留4位
	 * @param average1 保留下属位数之后的2位
	 * @param average2 保留小数位数之后的，2位
	 * @return
	 */
	public static double dfaL(double dfa, double average2, int... scales) {
		return MathUtils.round(MathUtils.div(dfa, average2), scales);
	}

	
	/**
	 * 计算一个科目所有的小题难度，或者整个考试所有科目的难度指数
	 * @param segments，科目10分一段，小题5分一段
	 * @param list 带计算学生成绩列表，包含两个属性，一个是总分t2totalscore，一个单科成绩t2score（或者一个是单科成绩，一个是小题成绩）
	 * @param totalscoreFun 总成绩或者单科成绩
	 * @param t2score 单科成绩或者小题成绩
	 * @param full 总分或者单科满分
	 * @return 分数-难度指数
	 */
	public static <T,U,X> List<Map<String,Object>> difficultyIndex(List<Double> segments,List<T> list, Function<T, Double> tscoreFun,Function<T, Long> tstudentIDFun,List<U> items,Function<U,Long> uidFun,Function<U,Double> fullScoreFun, Function<U,List<X>> studentQuestions,Function<X,Long> xstudentIDFun,Function<X,Double> xScoreFun) {
		Map<Double, List<Long>> map=LambdaUtils.groupby(list,x->ToolUtils.key(segments, tscoreFun.apply(x), true,true),Collectors.mapping(tstudentIDFun, Collectors.toList()));
		map=LambdaUtils.whole4group(map, segments, x->x, new ArrayList<>());//每个分数段有这些学生
		
		List<Map<String,Object>> result =new ArrayList<>();
		for(U u:items) {//每道小题
			List<X> sestudentQuestions =studentQuestions.apply(u);//小题的学生
			List<Map<String,Object>> questionResult=difficultyIndex(map,sestudentQuestions, xstudentIDFun,xScoreFun, fullScoreFun.apply(u),uidFun.apply(u));
			result.addAll(questionResult);
		}
		return result;
	}
	
	/**
	 * 计算一个科目，或者一个小题的难度指数
	 * @param segments，科目10分一段，小题5分一段
	 * @param list 带计算学生成绩列表，包含两个属性，一个是总分t2totalscore，一个单科成绩t2score（或者一个是单科成绩，一个是小题成绩）
	 * @param t2totalscore 总成绩或者单科成绩
	 * @param t2score 单科成绩或者小题成绩
	 * @param full 总分或者单科满分
	 * @return 分数-难度指数
	 */
	public static <T,U> List<Map<String,Object>> difficultyIndex(List<Double> segments,List<T> list, Function<T, Double> tscoreFun, Function<T, Long> tstudentIDFun, List<U> ulist,Function<U, Long> ustudentIDFun,Function<U, Double> uscoreFun, double full) {
		if (full == 0 || segments.isEmpty()) {
			return new ArrayList<>();
		}
		Map<Double, List<Long>> map=LambdaUtils.groupby(list,x->ToolUtils.key(segments, tscoreFun.apply(x), true,true),Collectors.mapping(tstudentIDFun, Collectors.toList()));//这个分组每个小题都要算一次不太好，每个科目算一次
		map=LambdaUtils.whole4group(map, segments, x->x, new ArrayList<>());
		return difficultyIndex(map, ulist, ustudentIDFun, uscoreFun, full, 0l);
	}
	
	/**
	 * 计算一个科目，或者一个小题的难度指数
	 * @param segmentStudents  一个分段有多少人 score-学生IDs
	 * @param list 带计算的列表，一般是sestudentsubject|sequestionstudent
	 * @param studentIDFun 学生ID
	 * @param scoreFun 学生分数
	 * @param full 满分
	 * @param id
	 * @return
	 */
	public static <T> List<Map<String,Object>> difficultyIndex(Map<Double, List<Long>> segmentStudents, List<T> list,Function<T, Long> studentIDFun,Function<T, Double> scoreFun, double full,long id) {
		Map<Long, T> map=LambdaUtils.list2map(list, studentIDFun);
		List<Map<String,Object>> result=new ArrayList<>();
		for(Map.Entry<Double,List<Long>> entry:segmentStudents.entrySet()) {
			double average = entry.getValue().stream().mapToDouble(x -> map.containsKey(x)?scoreFun.apply(map.get(x)):0).average().orElse(0);
			double difficulty = MathUtils.div(average, full);
			Map<String,Object> item =new HashMap<>();
			item.put("id", id);
			item.put("score", entry.getKey());
			item.put("value", difficulty);
			result.add(item);
		}
		return result;
	}
	
	
	/**
	 * 计算score表，分数的排名，数量，sum（例如 130对应的是 (130,满分]的人数）
	 * @param objects //带计算得列表，要有分数数据
	 * @param scorefun  分数数据
	 * @return
	 */
	public static <T> List<Map<String, Object>> computeScore(List<T> objects, Function<T, Double> scorefun)
	{
		Map<Double, Long> group = LambdaUtils.groupby(objects, scorefun, Collectors.counting());
		List<Map<String, Object>> result = new ArrayList<Map<String, Object>>();

		int lastOrder = 1;
		int lastCount = 0;
		int lastSum = 0;

		for (double score : group.keySet())
		{
			Map<String, Object> map = new HashMap<>();

			int order = lastOrder + lastCount;//
			int sum = lastCount + lastSum;//
			int count = Integer.parseInt(String.valueOf(group.get(score)));

			map.put("Score", score);
			map.put("COrder", order);
			map.put("CCount", count);
			map.put("CSum", sum);
			result.add(map);

			lastOrder = order;
			lastCount = count;
			lastSum = sum;
		}
		return result;
	}
	
	
	
	
	/**
	 * 计算分数段，可以自定义收集方式,一般其他的用score表计算，班级的可以用这个方法计算因为班级没有score表
	 * @param top
	 * @param bottom
	 * @param step
	 * @param objects
	 * @param scoreExtractor
	 * @param c
	 * @param withSums
	 * @return
	 */
	@SafeVarargs
	public static <T, U> List<Map<String, Object>> computeSegments(double top, double bottom, double step, List<T> objects, Function<T, Double> scoreExtractor, Collector<T, ?, ?>... collectors)
	{
		Collector<T, ?, ?> collector = Collectors.counting();
		if (collectors != null && collectors.length > 0)
		{
			collector = collectors[0];
		}
		
		List<Double> temp = getSegments(top, bottom, step);
		List<Double> segments = new ArrayList<>(temp);
		if (!segments.contains(0d))
		{
			segments.add(0d);
		}
		Map<Double, ?> map = LambdaUtils.groupby(objects, x -> ToolUtils.key(segments, scoreExtractor.apply(x), false, false), collector);
		
		List<Map<String, Object>> list = new ArrayList<>();
		int size=segments.size();
		for (int i = 0; i <size;  i++)
		{
			double key = segments.get(i);
			Integer count = 0;
			Object o = map.get(key);
			if (o != null)
			{
				count = Integer.parseInt(o.toString());
			}

			Map<String, Object> onemap = new HashMap<>();
			onemap.put("id", key);
			onemap.put("count", count);
			if (i == 0)
			{
				onemap.put("desc", MathUtils.format(key) + "以上");
				onemap.put("min", key);
				onemap.put("max", Integer.MAX_VALUE);
			}
			else if (i == size-1)
			{
				onemap.put("desc", MathUtils.format(segments.get(i - 1)) + "以下");
				onemap.put("min", Integer.MIN_VALUE);
				onemap.put("max", segments.get(i - 1));
			}
			else
			{
				onemap.put("desc", MathUtils.format(key) + "-" + MathUtils.format(segments.get(i - 1)));
				onemap.put("min", key);
				onemap.put("max", segments.get(i - 1));
			}
			list.add(onemap);
		}

		// 累计
		int sum = 0;
		for (Map<String, Object> onemap : list)
		{
			int number = (int) onemap.get("count");
			sum += number;
			onemap.put("sum", sum);
		}
		return list;
	}

	/**
	 * 计算分数段，需要基础表 score
	 * @param top
	 * @param bottom
	 * @param step
	 * @param objects
	 * @param scoreExtractor
	 * @param countExtractor
	 * @param withSums
	 * @return
	 */
	public static <T, U> List<Map<String, Object>> computeSegments(double top, double bottom, double step, List<T> objects, Function<T, Double> scoreExtractor, ToIntFunction<T> countExtractor)
	{
		List<Map<String, Object>> list = computeSegments(top, bottom, step, objects, scoreExtractor, Collectors.summingInt(countExtractor));
		return list;
	}
	
	
	/**
	 * 获取分段
	 * @param from
	 *            开始值
	 * @param to
	 *            结束值
	 * @param step
	 *            步长
	 * @param addZero
	 *            是否加0，默认不加
	 * @return
	 */
	private static List<Double> getSegments(double from, double to, double step, boolean... addZeros)
	{

		boolean addzreo = false;
		if (addZeros != null && addZeros.length > 0)
		{
			addzreo = addZeros[0];
		}

		if (from == to)
		{
			if (addzreo && from != 0)
			{
				return Arrays.asList(from, 0d);
			}
			return Arrays.asList(from);
		}

		List<Double> list = new ArrayList<>();
		if (from > to)
		{
			while (from > to)
			{
				list.add(from);
				from = from - step;
			}
			if (!list.contains(to))
			{
				list.add(to);
			}

			if (addzreo && !list.contains(0d))
			{
				list.add(0d);
			}

		}
		else
		{
			while (from < to)
			{
				list.add(from);
				from = from + step;
			}
			if (!list.contains(to))
			{
				list.add(to);
			}

			if (addzreo && !list.contains(0d))
			{
				list.add(0, 0d);
			}
		}

		return list;
	}
	
	
	
	//整个科目小题分组
	@Deprecated
	public static <T> List<Map<String, Object>> subjectQuestionGroup(long subjectID,String subjectName,List<T> questions,ToDoubleFunction<T> scoreFun,Function<T,Integer> typeFun,Function<T,String> titleFun,List<Map<String, Object>> list) {
		//questions=LambdaUtils.filter(questions,  x->x.getLevel().length()==8);
		
		List<Map<String, Object>> groupby=ComputeUtils.questionGroup(questions,scoreFun,typeFun,titleFun,list);
//		for(Map<String, Object> item:groupby) {
//			item.put("subjectName", subjectName);
//			item.put("subjectID", subjectID);
//		}
		Map<Integer, Map<Integer, Map<String, Object>>> map=LambdaUtils.groupbymap(groupby, x->Convert.toInt(x.get("id")), x->Convert.toInt(x.get("questionType")));
		
		List<Map<String, Object>> result =new ArrayList<>();
		
		for(int id:map.keySet()) {
			
		}
		
//		for(int type:types) {
//			map.get(type);
//		}
		return groupby;
	}
	
	
	/**
	 * 用于饼状图得小题难度区分度统计
	 * @param questions
	 * @param scoreFun
	 * @param typeFun
	 * @param titleFun
	 * @param list
	 * @return
	 */
	@Deprecated
	public static <T> Map<String, Map<Integer, Map<String, Object>>> questionGroupInfo(List<T> questions,ToDoubleFunction<T> scoreFun,Function<T,Integer> typeFun,Function<T,String> titleFun,List<Map<String, Object>> list) {
		List<Map<String, Object>> groupby=ComputeUtils.questionGroup(questions,scoreFun,typeFun,titleFun,list);
		Map<String, Map<Integer, Map<String, Object>>> map=LambdaUtils.groupbymap(groupby, x->Convert.toStr(x.get("value")), x->Convert.toInt(x.get("questionType")));
		return map;
	}
	
	
	
	
	@Deprecated
	@SuppressWarnings("unchecked")
	private static <T> Function<T, Map<String, Object>> getQuestionGroupFunction(ToDoubleFunction<T> scoreFun,List<Map<String, Object>> predicates) {
		Function<T, Map<String, Object>> fun=a->{
			double score=scoreFun.applyAsDouble(a);
			for (Map<String, Object> map : predicates) {
				Predicate<Double> redicate=(Predicate<Double>) map.get("predicate");
				boolean test =redicate.test(score);
				if(test) {
					return map;
				}
			}
			return null;
		};
		
		return fun;
	}
	@Deprecated
	public static <T> List<Map<String, Object>> questionGroupNewNewNew(List<T> seQuestions,ToDoubleFunction<T> scoreFun,Function<T,Integer> typeFun,Function<T,String> titleFun,List<Map<String, Object>> predicates){
		List<Integer> types= Arrays.asList(0,1,2);//主客总
		Map<Integer, Map<Map<String, Object>, List<T>>> result=LambdaUtils.groupby2(seQuestions,typeFun,getQuestionGroupFunction(scoreFun,predicates));
		result.put(2, LambdaUtils.groupby(seQuestions, getQuestionGroupFunction(scoreFun,predicates)));
		
		List<Map<String, Object>> info =new ArrayList<>();
		for(int type:types) {
			Map<Map<String, Object>, List<T>> map=result.get(type);
			if(map==null) {
				map=LambdaUtils.list2map(predicates, g->g, x->new ArrayList<>());
			}
			
			Map<String, Object> item =new HashMap<>();
			double score= map.values().stream().mapToDouble(x->x.stream().mapToDouble(scoreFun).reduce(0d, Double::sum)).reduce(0d, Double::sum);
			int number= map.values().stream().mapToInt(x->x.size()).reduce(0, Integer::sum);
			item.put("type", type);
			item.put("typeName", type);
			item.put("number", number);
			item.put("score", score);
			
			Map<Map<String, Object>, List<T>> finalMap = map;
			LambdaUtils.list2map(predicates, x->x.get("id").toString(), x->{
				List<T> ts=finalMap.get(x);
				if(ts==null) {
					ts=new ArrayList<>();
				}
				Map<String, Object> typeitem =new HashMap<>();
				
				int groupnumber=ts.size();
				double groupscore=ts.stream().mapToDouble(scoreFun).reduce(0d, Double::sum);
				typeitem.put("number", groupnumber);
				typeitem.put("numberrate", MathUtils.divPercent(groupnumber, number));
				typeitem.put("score", groupscore);
				typeitem.put("scorerate", MathUtils.divPercent(groupscore, score));
				typeitem.put("questionnames", LambdaUtils.list2list(ts, titleFun));
				return typeitem;
			}).forEach((key,value)->{
				item.put(key, value);
			});
			
			info.add(item);
		}
		return info;
	}
	
	
	/**
	 * // 小题分组，主要用于难度和区分度分组+主观题，客观题，总体分组
	 * @param seQuestions 小题
	 * @param scoreFun 分数
	 * @param typeFun 主客观
	 * @param titleFun 题目
	 * @param predicates 划分标准
	 * @return [难题-主观题；难题-客观题；难题-合计；简单题-主观题....]
	 */
	@Deprecated
	@SuppressWarnings("unchecked")
	public static <T> List<Map<String, Object>> questionGroup(List<T> seQuestions,ToDoubleFunction<T> scoreFun,Function<T,Integer> typeFun,Function<T,String> titleFun,List<Map<String, Object>> predicates)
	{
		//seQuestions=LambdaUtils.filter(seQuestions,  x->x.getLevel().length()==8);
		
		List<Integer> types=Arrays.asList(1,0,2);//客观题，主观题，合计
		
		Map<Integer, Double> scoremap=LambdaUtils.groupby(seQuestions, typeFun,Collectors.summingDouble(scoreFun));//主客观题得分数，小题累加，可能超过满分（有选做题）
		scoremap=LambdaUtils.whole4group(scoremap, types, x->x, 0d);
		scoremap.put(2, seQuestions.stream().mapToDouble(scoreFun).sum());
		
		Map<Integer, Long> numbermap=LambdaUtils.groupby(seQuestions, typeFun,Collectors.counting());//主客观题小题数量
		numbermap=LambdaUtils.whole4group(numbermap, types, x->x, 0l);
		numbermap.put(2, Convert.toLong(seQuestions.size()));
		
		List<Map<String, Object>> result= new ArrayList<>();
		for (Map<String, Object> map : predicates)
		{
			@SuppressWarnings("rawtypes")
			List<T> questions = LambdaUtils.filter(seQuestions, (Predicate) map.get("predicate"));
			if (questions == null)
			{
				questions = new ArrayList<>();
			}
			
			Map<Integer, List<T>> group=LambdaUtils.groupby(questions, typeFun);//主观题和客观题
			group=LambdaUtils.whole4group(group,types, x->x, new ArrayList<>());
			group.put(2, questions);
			
			for(int type:group.keySet()) {
				List<T> typequestions=group.get(type);
				
				Map<String, Object> item =new HashMap<>(map);
				item.put("questionType", type);
				item.put("questionTypeName", "合计");
				if(type!=2) {
					item.put("questionTypeName", type==0?"非客观题":"客观题");
				}
				
				double score= typequestions.stream().mapToDouble(scoreFun).sum();
				double totalScoreType= scoremap.containsKey(type)?scoremap.get(type):0;
				item.put("score",score);
				item.put("totalScore",totalScoreType);
				item.put("scoreRate", MathUtils.divPercent(score, totalScoreType));
				
				long totalNumberType=numbermap.containsKey(type)?numbermap.get(type):0;
				item.put("number", typequestions.size());
				item.put("totalNumber", totalNumberType);
				item.put("numberRate", MathUtils.divPercent(typequestions.size(), totalNumberType));
				
				item.put("questionNames", LambdaUtils.list2list(typequestions, titleFun));
				
				item.remove("predicate");
				result.add(item);
			}
		}
		return result;
	}
	
	/**
	 * 饼状图的难度|区分度
	 * @param seQuestions
	 * @param scoreFun
	 * @param typeFun
	 * @param titleFun
	 * @param predicates
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static <T> List<Map<String, Object>> questionGroupNew(List<T> list,ToDoubleFunction<T> scoreFun,Function<T,Integer> typeFun,Function<T,String> titleFun,List<Map<String, Object>> predicates){
		List<Integer> types= Arrays.asList(0,1);//主客
		Map<Map<String, Object>, Map<Integer, List<T>>> result= LambdaUtils.groupby2whole(list, predicates, x->x, x-> (Predicate<T>)x.get("predicate"), types, x->x,x->x,typeFun);
		
		double totalScore =list.stream().mapToDouble(scoreFun).sum();
		List<Map<String, Object>> datas=LambdaUtils.list2list(result.entrySet(), entry->{
			Map<String, Object> item =new HashMap<>();
			double score= entry.getValue().values().stream().mapToDouble(x->x.stream().mapToDouble(scoreFun).reduce(0d, Double::sum)).reduce(0d, Double::sum);
			item.put("name", entry.getKey().get("name"));
			item.put("desc", entry.getKey().get("value"));
			item.put("score", score);
			item.put("scoreRate", MathUtils.divPercent(score, totalScore));
			LambdaUtils.list2map(types, x->x, x->{
				List<T> ts=entry.getValue().get(x);
				Map<String, Object> typeitem =new HashMap<>();
				typeitem.put("questionnames", LambdaUtils.list2list(ts, titleFun));
				return typeitem;
			}).forEach((key,value)->{
				item.put(key+"", value);
			});
			return item;
		});
		return datas;
	}
	
	/**
	 * 
	 * @param seQuestions
	 * @param scoreFun
	 * @param typeFun
	 * @param titleFun
	 * @param predicates
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static <T> List<Map<String, Object>> questionGroupNewNew(List<T> list,ToDoubleFunction<T> scoreFun,Function<T,Integer> typeFun,Function<T,String> titleFun,List<Map<String, Object>> predicates){
		List<Integer> types= Arrays.asList(0,1,2);//主客总
		Map<Integer, Map<Map<String, Object>, List<T>>> result= LambdaUtils.groupby2whole(list, types, x->x,x->xx->typeFun.apply(xx)==x,predicates,x->x,x-> (Predicate<T>)x.get("predicate"));
		result.put(2, LambdaUtils.groupbywhole(list, predicates, x->x, x->(Predicate<T>)x.get("predicate")));
		
		return LambdaUtils.list2list(result.entrySet(), entry->{
			Map<String, Object> item =new HashMap<>();
			double score= entry.getValue().values().stream().mapToDouble(x->x.stream().mapToDouble(scoreFun).reduce(0d, Double::sum)).reduce(0d, Double::sum);
			int number= entry.getValue().values().stream().mapToInt(x->x.size()).reduce(0, Integer::sum);
			item.put("type", entry.getKey());
			item.put("typeName", entry.getKey());
			item.put("number", number);
			item.put("score", score);
			
			LambdaUtils.list2map(predicates, x->x.get("id").toString(), x->{
				List<T> ts=entry.getValue().get(x);
				Map<String, Object> typeitem =new HashMap<>();
				
				int groupnumber=ts.size();
				double groupscore=ts.stream().mapToDouble(scoreFun).reduce(0d, Double::sum);
				typeitem.put("number", groupnumber);
				typeitem.put("numberrate", MathUtils.divPercent(groupnumber, number));
				typeitem.put("score", groupscore);
				typeitem.put("scorerate", MathUtils.divPercent(groupscore, score));
				typeitem.put("questionnames", LambdaUtils.list2list(ts, titleFun));
				return typeitem;
			}).forEach((key,value)->{
				item.put(key, value);
			});
			
			return item;
		});
	}
	
	/**
	 * 均衡发展分析
	 * @param list 带计算列表，要求有平均分，标准差
	 * @param totalaverage 参考平均分
	 * @param standardAverage 参考标准差
	 * @param funAverage 平均分
	 * @param funStandard 标准差
	 * @return
	 * @throws Exception
	 */
	public static <T> List<Map<String, Object>> getBalances(List<T> list,Function<T,String> nameFun, double totalaverage, double standardAverage, Function<T, Double> funAverage, Function<T, Double> funStandard) throws Exception
	{
		List<Map<String, Object>> result =new ArrayList<>();
		list.forEach(x->{
			Map<String, Object> item =new HashMap<>();
			double average = funAverage.apply(x);
			double standard = funStandard.apply(x);
			item.put("name", nameFun.apply(x));
			item.put("average", average);
			item.put("standard", standard);
			String average_standard_info="";
			if (average >= totalaverage && standard < standardAverage)
			{
				average_standard_info="高水平、高均衡";
			}
			if (average >= totalaverage && standard >= standardAverage)
			{
				average_standard_info="高水平、低均衡";
			}
			if (average < totalaverage && standard < standardAverage)
			{
				average_standard_info="低水平、高均衡";
			}
			if (average < totalaverage && standard >= standardAverage)
			{
				average_standard_info="低水平、低均衡";
			}
			item.put("average_standard_info", average_standard_info);
			result.add(item);
		});
		return result;
	}
	
	/**
	 * 均衡发展分析
	 * @param list 带计算列表，要求有平均分，标准差
	 * @param totalaverage 参考平均分
	 * @param standardAverage 参考标准差
	 * @param funAverage 平均分
	 * @param funStandard 标准差
	 * @return
	 * @throws Exception
	 */
	public static <T> Map<String, List<T>> getBalances(List<T> list, double totalaverage, double standardAverage, Function<T, Double> funAverage, Function<T, Double> funStandard) throws Exception
	{
		List<String> names = Arrays.asList("高水平、高均衡", "高水平、低均衡",  "低水平、低均衡","低水平、高均衡");
		Map<String, List<T>> map = LambdaUtils.groupby(list, x -> {
			double average = funAverage.apply(x);
			double standard = funStandard.apply(x);
			if (average >= totalaverage && standard < standardAverage)
			{
				return names.get(0);
			}
			if (average >= totalaverage && standard >= standardAverage)
			{
				return names.get(1);
			}
			if (average < totalaverage && standard < standardAverage)
			{
				return names.get(3);
			}
			if (average < totalaverage && standard >= standardAverage)
			{
				return names.get(2);
			}
			return "";
		});
		map=LambdaUtils.whole4group(map, names, x->x, new ArrayList<>());
		return map;
	}
	
	
	public static void main(String[] args) {
		double score=0.5;
		List<Double> segments =new ArrayList<>();
		double segmentNum=score>=5?5:Math.floor(score);
		double div = score>=5?(int)MathUtils.div(score, 5):1;
		for(int i=0;i<segmentNum;i++) {
			segments.add(i*div);
		}
		if(!segments.contains(score)) {
			segments.add(score);
		}if(!segments.contains(0d)) {
			segments.add(0,0d);
		}
		
		System.out.println(segments);
		
		
		double result=ToolUtils.key(segments,-10, true, true);
		System.out.println(result);
		
		double d=-1.4d;
		System.out.println(Math.floor(d));
	}
	
	
	
	/**
	 * //客观题选项统计，主观题得分统计
	//学生小题得分也可以用score表，这样统计每个小题的得分会简单，客观题存储另外一个表格？
	 * 学生每道题的得分统计
	 * @param score 该题目满分
	 * @param list 改题目的学生得分，也可以是score表（当前没有）
	 * @return
	 */
	public static <T, U> List<Map<String, Object>> questionScoreSegment(double score, List<Double> list)
	{
		List<Double> segments =new ArrayList<>();
		double segmentNum=score>=5?5:Math.floor(score);
		double div = score>=5?(int)MathUtils.div(score, 5):1;
		for(int i=0;i<segmentNum;i++) {
			segments.add(i*div);
		}
		if(!segments.contains(score)) {
			segments.add(score);
		}
		if(!segments.contains(0d)) {
			segments.add(0,0d);
		}
		Map<Double, Long> map=LambdaUtils.groupby(list,  x -> ToolUtils.key(segments,x, true, true), Collectors.counting());
		
		
		List<Map<String,Object>> result =new ArrayList<>();
		Collections.reverse(segments);//从大到小
		
		List<Double> full=LambdaUtils.filter(list, x->x==score);
		if(!full.isEmpty()) {
			Map<String,Object> item =new HashMap<>();
			item.put("name", "满分");
			item.put("value", full.size());
			item.put("rate", MathUtils.divPercent(full.size(),list.size()));
			result.add(item);
			map.put(score, map.get(score)-full.size());
		}
		
		for(int i=0;i<segments.size();i++) {
			double segment=segments.get(i);
			if(!map.containsKey(segment) || map.get(segment)==0) {
				continue;
			}
			Map<String,Object> item =new HashMap<>();
			String name =(i==segments.size()-1)?"零分":MathUtils.formatZero(segments.get(i+1))+"-"+MathUtils.formatZero(segment)+"分";
			item.put("name", name);
			item.put("value", map.get(segment));
			item.put("rate", MathUtils.divPercent( map.get(segment),list.size()));
			result.add(item);
		}
		
		return result;
	}
	
	
	/**
	 * 各领域汇总，得分率差异值
	 * @param items 一般是一次考试一个科目的知识点
	 * @param idFun 知识点的id
	 * @param tavgFun 知识点的平均分
	 * @param scoreFun 知识点的满分
	 * @param itemItems 一般是各个学校或者区县的 知识点情况
	 * @param utidFun 对应的每个知识点id
	 * @param avgFun 每个学校或者区县的平均分
	 * @return
	 */
	public static <T, U> Map<Long, Double> maxdifficulty(List<T> items,Function<T,Long> idFun,Function<T,Double> tavgFun,Function<T,Double> scoreFun,List<U> itemItems,Function<U,Long> uidFun,Function<U,Double> avgFun,Function<U,Long> utidFun){
		Map<Long, List<U>> map=LambdaUtils.groupby(itemItems, utidFun);
		Map<Long, Double> result =new LinkedHashMap<>();
		for(T t:items) {
			
			long id=idFun.apply(t);
			List<U> us=map.get(id);
			double score=scoreFun.apply(t);//整体满分
			double average=tavgFun.apply(t);//整体平均分
			double diff=MathUtils.divPercent(average, score);//整体得分率，百分制2位小数
			if(us==null) {
				continue;
			}
			
			Map<Long,Double> itemMap=new HashMap<>();//每个学校得分率和总体的差值
			double max_difficulty=0;
			for(U u:us) {
				double diffitem=MathUtils.divPercent(avgFun.apply(u), score);//各个学校的得分率，百分制2位小数
				double value=MathUtils.sub(diffitem,diff);
				itemMap.put(uidFun.apply(u), value);
				double value_abs=Math.abs(value);
				if(value_abs>max_difficulty) {
					max_difficulty=value_abs;
				}
			}
			max_difficulty=MathUtils.round(MathUtils.mul(max_difficulty,0.25d),2);//总体的得分率差异值
			result.put(id, max_difficulty);
			
			Map<Long,Double> infos=new HashMap<>();//每个学校的显示对号，错号数量
			for(U u:us) {
				double value=MathUtils.div(itemMap.get(uidFun.apply(u)), max_difficulty);
				infos.put(uidFun.apply(u), value>0?Math.ceil(value):Math.floor(value));
			}
		}
		return result;
	}
	
	/**
	 * 单科缺考 0分&&（无卡或者涂了缺考）
	 * @param seStudentSubjects 学生信息
	 * @param scoreFun 分数
	 * @param haveImageFun 答题卡情况
	 * @param schoolIDFun 学校ID
	 * @param studentNameFun 学生姓名
	 * @return
	 */
	public static <T, U> List<Map<String, Object>> missSubject(List<T> seStudentSubjects,Function<T,Double> scoreFun,Function<T,Double> haveImageFun,Function<T,Long> schoolIDFun,Function<T,String> studentNameFun){
		//整体每个学校分组
		Map<Long, List<T>> sestudentsubjectmap = LambdaUtils.groupby(seStudentSubjects,schoolIDFun);
		
		//缺考
		List<Map<String, Object>> result = new ArrayList<>();
		List<T> misss = LambdaUtils.filter(seStudentSubjects, x -> {
			double score = scoreFun.apply(x);
			double haveImage = haveImageFun.apply(x);
			return score == 0 && (haveImage == 0 || haveImage == 2);//无卡或者缺考的
		});
		Map<Long, List<T>> map = LambdaUtils.groupby(misss,schoolIDFun);
		
		
		if (!misss.isEmpty()) {
			Map<String, Object> item = new HashMap<>();
			item.put("schoolid", 0);
			item.put("schoolcode", "-");
			item.put("schoolname", "整体");
			item.put("studentnumber", seStudentSubjects.size());
			item.put("missnumber", misss.size());
			item.put("missrate", MathUtils.divPercent(misss.size(), seStudentSubjects.size()) );
			item.put("missnames", "-");
			result.add(item);

			List<Map<String, Object>> subresult = new ArrayList<>();
			for (Entry<Long, List<T>> entry : map.entrySet()) {
				long schoolid = entry.getKey();
				List<T> students = entry.getValue();
				item = new HashMap<>();
				item.put("schoolid", schoolid);
				item.put("schoolcode", "");
				item.put("schoolname", "");
				item.put("studentnumber", sestudentsubjectmap.get(schoolid).size());
				item.put("missnumber", students.size());
				item.put("missrate", MathUtils.divPercent(students.size(), sestudentsubjectmap.get(schoolid).size()));
				item.put("missnames", LambdaUtils.list2list(students, studentNameFun));
				subresult.add(item);
			}
			
			subresult.sort((x,y)->{
				int miss1=Convert.toInt(x.get("missnumber"));
				int miss2=Convert.toInt(y.get("missnumber"));
				if(miss1==miss2) {
					return x.get("schoolcode").toString().compareTo(y.get("schoolcode").toString());//这里还没有code
				}
				return miss2-miss1;
			});
			
			result.addAll(subresult);
		}
		
		
		return result;
	}
	
	/**
	 * 计算每个区域有多少学生
	 * @param groupSchools
	 * @param list
	 * @param schoolIDFun
	 * @return
	 */
	public static <T> Map<Long,List<T>> groupSchool(Map<Long,List<Long>> groupSchools, List<T> list,Function<T,Long> schoolIDFun)
	{
		Map<Long,List<T>> result =new LinkedHashMap<>();
		for(Entry<Long, List<Long>> entry:groupSchools.entrySet()) {
			List<Long> schoolIDs=entry.getValue();
			List<T> items=LambdaUtils.filter(list, x->schoolIDs.contains(schoolIDFun.apply(x)));
			result.put(entry.getKey(), items);
		}
		return result;
	}
	
	//////////////////////////////////////////////////////////////////////下面的待完善////////////////////////////////////////////////////////////////////////////////////////
	
	//缺考
	//全科优秀，及格率
	//历次考试对比
	//发展性分析
	//临界
	//topn
	
	//上线计算
	//rate的计算
	//qg的计算
	//question的计算
	
	//原则是按照数据库表计算；
	
	//在一个表里的都统一计算（例如平均分，标准差等），不在一个表的，可以单独计算（录入测试特征，小题，上线，rate，topn，level，difficulty，score等）
	//范围：考试，区县，学校，班级：决定计算哪些学生
	//维度：考试，单科
	//维度：测试特征，小题
	//指标：平均分，标准差...
	//维度：上线，rate，topn，level
	//中间：difficulty，score
	
	
	//测试特征按照小题计算
	//区县按照学校计算
	
	
	
	/**
	 * 区分度计算
	 * @param ts 一般是学生sestudentsubjects
	 * @param t2score 分数
	 * @param examCodeFun 小题满分
	 * @return 
	 */
	public static <T> double[] discrimination(List<T> ts,Function<T,Long> idFun,ToDoubleFunction<T> t2score,Function<T,String> examCodeFun) {
		//学生单科有效成绩的总人数
		int subjectStudentCount = ts.size();
		
		//高分区的学生数量和低分区的学生数量
		int upStudentCount = subjectStudentCount*27/100;
		int lowStudentCount = subjectStudentCount-subjectStudentCount*73/100;
		
		//ts排序
		//获取前面这些人的平均分
		double havg=ts.stream().limit(upStudentCount).collect(Collectors.averagingDouble(t2score));
		
		//ts倒序
		//获取后面这些人的平均分
		double lavg=ts.stream().limit(lowStudentCount).collect(Collectors.averagingDouble(t2score));
		
		return new double[] {havg, lavg};
	}
	
	
	/**
	 * 获取一个小题的区分度
	 * @param orderedStudentIds 排序好的的待计算的学生列表，一般是按照总分或者单科总分排序
	 * @param ts 一般是指 questionStudent
	 * @param t2studentID  
	 * @param t2questionID
	 * @param t2score
	 * @param score 小题满分
	 * @return  奇葩，statistics是去除0分的人数，orderedStudentIds是包含0分的
	 */
	public static <T> double[] discrimination(List<Long> orderedStudentIds, List<T> ts, Function<T, Long> t2studentID, Function<T, Float> t2score, float score) {
		int number = orderedStudentIds.size();
		if (number == 0) {
			return new double[] { 0, 0, 0, 0, 0 };
		}

		// 这里实现的有问题，hnum和lnum应该是一样的，比如100个人，那么前27% 和后27%是一样的都是 27个人。
		int hnum = number * 27 / 100;
		int lnum = number - number * 73 / 100;
		if (hnum + lnum > 3 && lnum > 0 && hnum > 0) {
			// 前27%
			List<Long> hstudentList = CollUtil.sub(orderedStudentIds, 0, hnum);
			Set<Long> sets = new HashSet<>(hstudentList);
			List<T> hs = LambdaUtils.filter(ts, x -> sets.contains(t2studentID.apply(x)));
			double havg = hs.stream().mapToDouble(x -> t2score.apply(x)).average().orElse(0);

			// 后27%
			List<Long> lstudentList = CollUtil.sub(orderedStudentIds, number - lnum, number);
			Set<Long> sets1 = new HashSet<>(lstudentList);
			List<T> ls = LambdaUtils.filter(ts, x -> sets1.contains(t2studentID.apply(x)));
			double lavg = ls.stream().mapToDouble(x -> t2score.apply(x)).average().orElse(0);

			double result = (havg - lavg) / score;
			return new double[] { hnum, havg, lnum, lavg, result };
		}

		return new double[] { 0, 0, 0, 0, 0 };
	}

	
	//信度
	void reliability(){
		
	}
	
	
	
	
}